/*****************************************************************************/
/**
 *  @file   main.cpp
 *  @author Naohisa Sakamoto
 *  @brief  Tensor visualization system used in JSCES 2012 paper.
 */
/*----------------------------------------------------------------------------
 *
 *  Copyright (c) Visualization Laboratory, Kyoto University.
 *  All rights reserved.
 *  See http://www.viz.media.kyoto-u.ac.jp/kvs/copyright/ for details.
 *
 *  $Id$
 */
/*****************************************************************************/
#include <kvs/CommandLine>
#include <kvs/RGBFormulae>
#include <kvs/glut/Application>
#include <kvs/glut/Screen>
#include <kvs/glut/Timer>
#include <kvs/glew/StochasticRenderingCompositor>
#include <kvs/glew/StochasticPointEngine>
#include <kvs/glew/StochasticLineEngine>
#include <kvs/glew/StochasticPolygonEngine>
#include "Parameter.h"
#include "Command.h"
#include "Event.h"
#include "Widget.h"


int main( int argc, char** argv )
{
    // Set and parse commandline options.
    kvs::CommandLine commandline( argc, argv );
    commandline.addHelpOption();
    commandline.addOption( "t", "Tensor volume data.", 1, false );
    commandline.addOption( "p", "Polygon data.", 1, false );
    commandline.addOption( "c", "Celltree data.", 1, false );
    if ( !commandline.parse() ) exit( EXIT_FAILURE );

    // Parameters.
    Parameter parameter;

    // Read commandline options.
    if ( commandline.hasOption("t") )
    {
        std::string filename = commandline.optionValue<std::string>("t");
        Command::ReadTensor( parameter, filename );
    }

    if ( commandline.hasOption("p") )
    {
        std::string filename = commandline.optionValue<std::string>("p");
        Command::ReadPolygon( parameter, filename );
    }

    if ( commandline.hasOption("c") )
    {
        std::string filename = commandline.optionValue<std::string>("c");
        Command::ReadCelltree( parameter, filename );
    }
    else { Command::CreateCelltree( parameter ); }

    // Create object.
    if ( parameter.polygon )
    {
        kvs::Vector3f min = parameter.polygon->minExternalCoord();
        kvs::Vector3f max = parameter.polygon->maxExternalCoord();
        kvs::Vector3ui res = kvs::Vector3ui( 10, 10, 10 );
        parameter.xmin = min.x();
        parameter.ymin = min.y();
        parameter.zmin = min.z();
        parameter.xmax = max.x();
        parameter.ymax = max.y();
        parameter.zmax = max.z();
        parameter.resx = res.x();
        parameter.resy = res.y();
        parameter.resz = res.z();
    }

    Command::CreatePoint( parameter );
    Command::CreateBounds( parameter, parameter.point );
    Command::CreateLine( parameter );

    // Event.
    kvs::glut::Timer timer( 30 );
    Event::TimerEvent timer_event;
    Event::KeyPressEvent key_press_event( parameter );

    // Application & screen.
    kvs::glut::Application app( argc, argv );
    kvs::glut::Screen screen( &app );
    screen.setTitle( "Hyperstreamlines" );
    screen.setSize( 800, 600 );
    screen.background()->setColor( kvs::RGBColor( 255, 255, 255 ) );
    screen.addTimerEvent( &timer_event, &timer );
    screen.addKeyPressEvent( &key_press_event );
    screen.show();

    // Compositor.
    parameter.compositor = new kvs::glew::StochasticRenderingCompositor( &screen );
    parameter.compositor->enableLODControl();

    kvs::glew::StochasticPolygonEngine* polygon_engine = NULL;
    if ( parameter.polygon )
    {
        polygon_engine = new kvs::glew::StochasticPolygonEngine();
        polygon_engine->setShader( kvs::Shader::BlinnPhong() );
        parameter.compositor->registerObject( parameter.polygon, polygon_engine );
    }

    kvs::glew::StochasticPointEngine* point_engine = NULL;
    kvs::glew::StochasticLineEngine* bounds_engine = NULL;
    if ( parameter.point )
    {
        point_engine = new kvs::glew::StochasticPointEngine();
        point_engine->disableShading();
        parameter.compositor->registerObject( parameter.point, point_engine );

        bounds_engine = new kvs::glew::StochasticLineEngine();
        bounds_engine->disableShading();
        parameter.compositor->registerObject( parameter.bounds, bounds_engine );
    }

    kvs::glew::StochasticLineEngine* line_engine = NULL;
    if ( parameter.line )
    {
        line_engine = new kvs::glew::StochasticLineEngine();
        line_engine->setShader( kvs::Shader::BlinnPhong() );
        line_engine->setOpacity( parameter.line_opacity );
        parameter.compositor->registerObject( parameter.line, line_engine );
    }

    // Widget for polygon.
    parameter.polygon_opacity_slider = new Widget::PolygonOpacitySlider( &screen, parameter );
    parameter.polygon_opacity_slider->setX( 10 );
    parameter.polygon_opacity_slider->setY( 5 );
    parameter.polygon_opacity_slider->setMargin( 5 );
    parameter.polygon_opacity_slider->setWidth( 130 );
    parameter.polygon_opacity_slider->setCaption( "Opacity [0,1]" );
    parameter.polygon_opacity_slider->setValue( 0.5 );
    parameter.polygon_opacity_slider->setRange( 0, 1 );
    parameter.polygon_opacity_slider->hideRange();
    parameter.polygon_opacity_slider->show();

    parameter.point_check_box = new Widget::PointCheckBox( &screen, parameter );
    parameter.point_check_box->setX( parameter.polygon_opacity_slider->x() );
    parameter.point_check_box->setY( parameter.polygon_opacity_slider->y() + 60 );
    parameter.point_check_box->setMargin( 5 );
    parameter.point_check_box->setCaption( "Seeds" );
    parameter.point_check_box->setState( true );
    parameter.point_check_box->show();

    parameter.bounds_check_box = new Widget::BoundsCheckBox( &screen, parameter );
    parameter.bounds_check_box->setX( parameter.point_check_box->x() );
    parameter.bounds_check_box->setY( parameter.point_check_box->y() + 20 );
    parameter.bounds_check_box->setMargin( 5 );
    parameter.bounds_check_box->setCaption( "Bounds" );
    parameter.bounds_check_box->setState( true );
    parameter.bounds_check_box->show();

    parameter.xscale_slider = new Widget::XScaleSlider( &screen, parameter );
    parameter.xscale_slider->setX( parameter.bounds_check_box->x() );
    parameter.xscale_slider->setY( parameter.bounds_check_box->y() + 20 );
    parameter.xscale_slider->setMargin( 5 );
    parameter.xscale_slider->setWidth( 130 );
    parameter.xscale_slider->setCaption( "Scale [0,1]" );
    parameter.xscale_slider->setValue( 1 );
    parameter.xscale_slider->setRange( 0, 1 );
    parameter.xscale_slider->hideRange();
    parameter.xscale_slider->show();

    parameter.yscale_slider = new Widget::YScaleSlider( &screen, parameter );
    parameter.yscale_slider->setX( parameter.xscale_slider->x() );
    parameter.yscale_slider->setY( parameter.xscale_slider->y() + 20 );
    parameter.yscale_slider->setMargin( 5 );
    parameter.yscale_slider->setWidth( 130 );
    parameter.yscale_slider->setCaption( "" );
    parameter.yscale_slider->setValue( 1 );
    parameter.yscale_slider->setRange( 0, 1 );
    parameter.yscale_slider->hideRange();
    parameter.yscale_slider->show();

    parameter.zscale_slider = new Widget::ZScaleSlider( &screen, parameter );
    parameter.zscale_slider->setX( parameter.yscale_slider->x() );
    parameter.zscale_slider->setY( parameter.yscale_slider->y() + 20 );
    parameter.zscale_slider->setMargin( 5 );
    parameter.zscale_slider->setWidth( 130 );
    parameter.zscale_slider->setCaption( "" );
    parameter.zscale_slider->setValue( 1 );
    parameter.zscale_slider->setRange( 0, 1 );
    parameter.zscale_slider->hideRange();
    parameter.zscale_slider->show();

    parameter.xtranslate_slider = new Widget::XTranslateSlider( &screen, parameter );
    parameter.xtranslate_slider->setX( parameter.zscale_slider->x() );
    parameter.xtranslate_slider->setY( parameter.zscale_slider->y() + 40 );
    parameter.xtranslate_slider->setMargin( 5 );
    parameter.xtranslate_slider->setWidth( 130 );
    parameter.xtranslate_slider->setCaption( "Translate [-1,1]" );
    parameter.xtranslate_slider->setValue( 0 );
    parameter.xtranslate_slider->setRange( -1, 1 );
    parameter.xtranslate_slider->hideRange();
    parameter.xtranslate_slider->show();

    parameter.ytranslate_slider = new Widget::YTranslateSlider( &screen, parameter );
    parameter.ytranslate_slider->setX( parameter.xtranslate_slider->x() );
    parameter.ytranslate_slider->setY( parameter.xtranslate_slider->y() + 20 );
    parameter.ytranslate_slider->setMargin( 5 );
    parameter.ytranslate_slider->setWidth( 130 );
    parameter.ytranslate_slider->setCaption( "" );
    parameter.ytranslate_slider->setValue( 0 );
    parameter.ytranslate_slider->setRange( -1, 1 );
    parameter.ytranslate_slider->hideRange();
    parameter.ytranslate_slider->show();

    parameter.ztranslate_slider = new Widget::ZTranslateSlider( &screen, parameter );
    parameter.ztranslate_slider->setX( parameter.ytranslate_slider->x() );
    parameter.ztranslate_slider->setY( parameter.ytranslate_slider->y() + 20 );
    parameter.ztranslate_slider->setMargin( 5 );
    parameter.ztranslate_slider->setWidth( 130 );
    parameter.ztranslate_slider->setCaption( "" );
    parameter.ztranslate_slider->setValue( 0 );
    parameter.ztranslate_slider->setRange( -1, 1 );
    parameter.ztranslate_slider->hideRange();
    parameter.ztranslate_slider->show();

    parameter.xresolution_slider = new Widget::XResolutionSlider( &screen, parameter );
    parameter.xresolution_slider->setX( parameter.ztranslate_slider->x() );
    parameter.xresolution_slider->setY( parameter.ztranslate_slider->y() + 40 );
    parameter.xresolution_slider->setMargin( 5 );
    parameter.xresolution_slider->setWidth( 130 );
    parameter.xresolution_slider->setCaption( "Resolution [0,50]" );
    parameter.xresolution_slider->setValue( 10 );
    parameter.xresolution_slider->setRange( 0, 50 );
    parameter.xresolution_slider->hideRange();
    parameter.xresolution_slider->show();

    parameter.yresolution_slider = new Widget::YResolutionSlider( &screen, parameter );
    parameter.yresolution_slider->setX( parameter.xresolution_slider->x() );
    parameter.yresolution_slider->setY( parameter.xresolution_slider->y() + 20 );
    parameter.yresolution_slider->setMargin( 5 );
    parameter.yresolution_slider->setWidth( 130 );
    parameter.yresolution_slider->setCaption( "" );
    parameter.yresolution_slider->setValue( 10 );
    parameter.yresolution_slider->setRange( 0, 50 );
    parameter.yresolution_slider->hideRange();
    parameter.yresolution_slider->show();

    parameter.zresolution_slider = new Widget::ZResolutionSlider( &screen, parameter );
    parameter.zresolution_slider->setX( parameter.yresolution_slider->x() );
    parameter.zresolution_slider->setY( parameter.yresolution_slider->y() + 20 );
    parameter.zresolution_slider->setMargin( 5 );
    parameter.zresolution_slider->setWidth( 130 );
    parameter.zresolution_slider->setCaption( "" );
    parameter.zresolution_slider->setValue( 10 );
    parameter.zresolution_slider->setRange( 0, 50 );
    parameter.zresolution_slider->hideRange();
    parameter.zresolution_slider->show();

    parameter.major_principal_button = new Widget::MajorPrincipalButton( &screen, parameter );
    parameter.major_principal_button->setX( parameter.zresolution_slider->x() );
    parameter.major_principal_button->setY( parameter.zresolution_slider->y() + 60 );
    parameter.major_principal_button->setMargin( 5 );
    parameter.major_principal_button->setCaption( "Major" );
    parameter.major_principal_button->setState( true );

    parameter.intermediate_principal_button = new Widget::IntermediatePrincipalButton( &screen, parameter );
    parameter.intermediate_principal_button->setX( parameter.major_principal_button->x() );
    parameter.intermediate_principal_button->setY( parameter.major_principal_button->y() + 20 );
    parameter.intermediate_principal_button->setMargin( 5 );
    parameter.intermediate_principal_button->setCaption( "Intermediate" );

    parameter.minor_principal_button = new Widget::MinorPrincipalButton( &screen, parameter );
    parameter.minor_principal_button->setX( parameter.intermediate_principal_button->x() );
    parameter.minor_principal_button->setY( parameter.intermediate_principal_button->y() + 20 );
    parameter.minor_principal_button->setMargin( 5 );
    parameter.minor_principal_button->setCaption( "Minor" );

    parameter.principal_button_group = new kvs::glut::RadioButtonGroup();
    parameter.principal_button_group->add( parameter.major_principal_button );
    parameter.principal_button_group->add( parameter.intermediate_principal_button );
    parameter.principal_button_group->add( parameter.minor_principal_button );
    parameter.principal_button_group->show();

    parameter.extract_button = new Widget::ExtractButton( &screen, parameter );
    parameter.extract_button->setCaption( "Extract" );
    parameter.extract_button->setX( parameter.minor_principal_button->x() );
    parameter.extract_button->setY( parameter.minor_principal_button->y() + 25 );
    parameter.extract_button->setWidth( 130 );
    parameter.extract_button->setHeight( 25 );
    parameter.extract_button->show();

    parameter.clear_button = new Widget::ClearButton( &screen, parameter );
    parameter.clear_button->setCaption( "Clear" );
    parameter.clear_button->setX( parameter.extract_button->x() );
    parameter.clear_button->setY( parameter.extract_button->y() + 27 );
    parameter.clear_button->setWidth( 130 );
    parameter.clear_button->setHeight( 25 );
    parameter.clear_button->show();

    parameter.orientation_axis = new Widget::OrientationAxis( &screen );
    parameter.orientation_axis->setSize( 120 );
    parameter.orientation_axis->setBoxType( kvs::glut::OrientationAxis::SolidBox );
    parameter.orientation_axis->enableAntiAliasing();
    parameter.orientation_axis->show();

    parameter.legend_bar = new Widget::LegendBar( &screen );
    parameter.legend_bar->setCaption( "Principal stress" );
    parameter.legend_bar->setWidth( 200 );
    parameter.legend_bar->setHeight( 70 );
    parameter.legend_bar->setColorMap( kvs::RGBFormulae::Rainbow( 256 ) );
    parameter.legend_bar->setRange( -1, 1 );
    parameter.legend_bar->show();

    return app.run();
}
